#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

struct ps
{
    int st;
    pthread_t *thr;
};

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int status = 0;

void *recvsocket(void *arg)
{
    struct ps *p = (struct ps*)arg;
    int st = p->st;
    char s[1024];
    
    while (1) {
        memset(s, 0, sizeof(s));
        int rc = recv(st, s, sizeof(s), 0);
        if (rc <= 0) {
            break;
        }
        printf("client: %s", s);
    }
    pthread_mutex_lock(&mutex);
    status = 0;
    pthread_mutex_unlock(&mutex);
    pthread_cancel(*(p->thr));
    return NULL;
}

void *sendsocket(void *arg)
{
    int st = *(int *)arg;
    char s[1024];
    while (1) {
        memset(s, 0, sizeof(s));
        read(STDIN_FILENO, s, sizeof(s));
        send(st, s, strlen(s), 0);
    }
    return  NULL;
}

int main(int arg, char *args[])
{
    if (arg < 2) {
        return -1;
    }
    
    int port = atoi(args[1]);
    int st = socket(AF_INET, SOCK_STREAM, 0);
    int on = 1;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
        printf("setsockopt failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    if (bind(st, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
        printf("bind faield %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    
    if (listen(st, 20) == -1) {
        printf("listen failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    int client_st = 0;
    struct sockaddr_in client_addr;
    
    pthread_t thrd1, thrd2;
    
    while (1) {
        memset(&client_addr, 0, sizeof(client_addr));
        socklen_t len = sizeof(client_addr);
        client_st = accept(st, (struct sockaddr *)&client_addr, &len);
        pthread_mutex_lock(&mutex);
        status++;
        pthread_mutex_unlock(&mutex);
        if (status > 1) {
            close(client_st);
            continue;
        }
        if (client_st == -1) {
            printf("accept failed %s\n", strerror(errno));
            return EXIT_FAILURE;
        }
        
        printf("accept by %s\n", inet_ntoa(client_addr.sin_addr));
        struct ps ps1;
        ps1.st = client_st;
        ps1.thr= &thrd2;
        pthread_create(&thrd1, NULL, recvsocket, &ps1);
        pthread_detach(thrd1);
        pthread_create(&thrd2, NULL, sendsocket, &client_st);
        pthread_detach(thrd2);
    }
    close(st);
    return EXIT_SUCCESS;
}
